﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using System.IO;

using Leap;

class Listener : Leap.Listener
{
    public override void OnConnect(Controller controller)
    {
        
        controller.EnableGesture(Gesture.GestureType.TYPE_CIRCLE);
        controller.EnableGesture(Gesture.GestureType.TYPE_KEY_TAP);
        controller.EnableGesture(Gesture.GestureType.TYPE_SCREEN_TAP);
        controller.EnableGesture(Gesture.GestureType.TYPE_SWIPE);
    }


    public override void OnFrame(Controller controller)
    {


        Frame frame = controller.Frame();


        foreach (var hand in frame.Hands)
        {
            HandSimpled hand_s = new HandSimpled(hand);
            if (CoreDetectionManager.delegate_fetcher != null)
            {
                CoreDetectionManager.delegate_fetcher(hand_s);
            }
            

            foreach (var signLanguageConfig in CoreDetectionManager.allSignLanguageConfig)
            {
                if (signLanguageConfig.CheckGesture(hand_s))
                {
                    if (CoreDetectionManager.delegate_sender != null)
                    {
                        CoreDetectionManager.delegate_sender(signLanguageConfig);
                    }
                    //SafeWriteLine(DateTime.Now.ToString("h:mm:ss ") + signLanguageConfig.Word);
                    break;
                }
            }
        }

    }
}

public class HandSimpled
{
    //The first digit is the thumb, followed by index finger, middle finger, ring finger, and little finger or pinky. 
    public int[,] bentPercent;
    public int[] gapStatus;
    public enum Gap { Overlaped, Closed, Opened };
    public enum HandJoint { BetweenIntermediateAndProximal, BetweenProximalAndPalm };
    public HandSimpled()
    {
        bentPercent = new int[5, 2];
        gapStatus = new int[4];
    }

    public HandSimpled(Hand thisHand) : this()
    {

        FingerList fingerList = thisHand.Fingers;
        if (!fingerList.IsEmpty)
        {
            //generate each bent percent
            foreach (var finger in fingerList)
            {
                /*
                [][][][]  Distal Phalanges
                [][][][]  Intermediate Phalanges
                [][][][]  Proximal Phalanges
                |       |   Palm, Metacarpals
                |       |/\
                |_________/  */

                Bone boneIntermediate = finger.Bone(Bone.BoneType.TYPE_INTERMEDIATE);
                Bone boneProximal = finger.Bone(Bone.BoneType.TYPE_PROXIMAL);
                Bone boneMetacarpals = finger.Bone(Bone.BoneType.TYPE_METACARPAL);
                double angleIntermediateToProximal = boneIntermediate.Direction.AngleTo(boneProximal.Direction);
                double angleProximalToMetacarpals = boneProximal.Direction.AngleTo(thisHand.Direction);

                double degIntermediateToProximal = 180 - (int)(angleIntermediateToProximal / (Math.PI / 2) * 90);
                double degProximalToMetacarpals = (int)(angleProximalToMetacarpals / (Math.PI / 2) * 90);

                bentPercent[(int)finger.Type, 0] = (int)(((180 - degIntermediateToProximal) / 90) * 100);
                bentPercent[(int)finger.Type, 1] = (int)(((180 - degProximalToMetacarpals) / 90) * 100);
            }

            //generate gap status
            for (int i = (int)Finger.FingerType.TYPE_THUMB; i < (int)Finger.FingerType.TYPE_PINKY; i++)
            {
                Bone boneFingerAProximal = fingerList[i].Bone(Bone.BoneType.TYPE_PROXIMAL);
                Bone boneFingerBProximal = fingerList[i + 1].Bone(Bone.BoneType.TYPE_PROXIMAL);
                //Solve bone's projection on palm

                //1. solve length of bone's projection on palm plain's normal    
                float boneAProjection = boneFingerAProximal.Direction.Dot(thisHand.Direction.Normalized);
                float boneBProjection = boneFingerBProximal.Direction.Dot(thisHand.Direction.Normalized);

                //2.generate vector parallel to palm
                Vector boneAParalleledToPalm = boneFingerAProximal.Direction + boneAProjection * thisHand.Direction.Normalized;
                Vector boneBParalleledToPalm = boneFingerBProximal.Direction + boneBProjection * thisHand.Direction.Normalized;

                double degOfGap = boneAParalleledToPalm.AngleTo(boneBParalleledToPalm) / (Math.PI / 2) * 90;
                Debug.WriteLine(degOfGap);

            }

            foreach (var percent in bentPercent)
            {
                Debug.Write(percent.ToString() + " ");
            }
            Debug.WriteLine("");
        }
    }
}

public class SignLanguageConfig
{
    public string Category;
    public string Word { get; set; }
    public int[,] BentPercentExpectation;
    public int[,] BentPercentPrecision;
    bool IsInitialized;

    public string GetPath()
    {
        string result = Environment.CurrentDirectory;
        result += "\\configs\\" + Word.Replace('.', '\\')+".sign_language_config";
        return result;
    }

    public void PutToFile()
    {
        string thisPath = this.GetPath();
        try
        {
            using (StreamWriter streamWriter = new StreamWriter(thisPath))
            {
                for(int i = 0; i < 5; i++)
                {
                    `
                }
                
            }
        }
    }

    public SignLanguageConfig()
    {
        BentPercentExpectation = new int[5, 2];
        BentPercentPrecision = new int[5, 2];
        IsInitialized = false;
    }

    public SignLanguageConfig(string fileName, string word) : this()
    {
        Word = word;
        Category = word.Split('.')[0];
        InitFromConfigFile(fileName);
    }

    void InitFromConfigFile(string fileName)
    {
        //data format: 5 fingers, each value is present as <precise value> <precision>
        List<string> data = FileReader.GetContentWithoutComment(fileName);
        if (data.Count == 5)
        {
            for (int i = 0; i < data.Count; i++)
            {
                try
                {
                    string[] fourValues = data[i].Split(' ');
                    if (fourValues.Count() >= 4)
                    {
                        BentPercentExpectation[i, 0] = int.Parse(fourValues[0]);
                        BentPercentPrecision[i, 0] = int.Parse(fourValues[1]);
                        if (BentPercentPrecision[i, 0] < 0)
                        {
                            IsInitialized = false;
                            return;
                        }

                        BentPercentExpectation[i, 1] = int.Parse(fourValues[2]);
                        BentPercentPrecision[i, 1] = int.Parse(fourValues[3]);
                        if (BentPercentPrecision[i, 1] < 0)
                        {
                            IsInitialized = false;
                            return;
                        }
                    }
                    else return;
                }
                catch
                {
                    return;
                }
            }
            IsInitialized = true;
        }
    }

    public bool CheckGesture(HandSimpled hand_s)
    {
        if (!IsInitialized) return false;
        for (int i = 0; i < 5; i++)
        {
            if (!(hand_s.bentPercent[i, 0] >= BentPercentExpectation[i, 0] - BentPercentPrecision[i, 0] &&
                hand_s.bentPercent[i, 0] <= BentPercentExpectation[i, 0] + BentPercentPrecision[i, 0] &&
                hand_s.bentPercent[i, 1] >= BentPercentExpectation[i, 1] - BentPercentPrecision[i, 1] &&
                hand_s.bentPercent[i, 1] <= BentPercentExpectation[i, 1] + BentPercentPrecision[i, 1]))
            {
                return false;
            }
        }
        return true;
    }

    public override string ToString()
    {
        string[] this_word_Split = this.Word.Split('.');
        return this_word_Split[this_word_Split.Count()-1];
    }
}

class FileReader
{
    public static List<string> GetContentWithoutComment(string fileName)
    {
        List<string> result = new List<string>();
        try
        {
            using (StreamReader streamReader = new StreamReader(fileName))
            {
                string line;
                while ((line = streamReader.ReadLine()) != null)
                {
                    if (line[0] != '#')
                    {
                        result.Add(line);
                    }
                }
            }
        }
        catch { }
        return result;
    }
}

class CoreDetectionManager
{
    public static List<string> configList;
    public static List<SignLanguageConfig> allSignLanguageConfig;

    public delegate void delegate_HandInfoFetcher(HandSimpled hand);
    public static delegate_HandInfoFetcher delegate_fetcher;

    public delegate void delegate_RecognitionInfoSender(SignLanguageConfig config);
    public static delegate_RecognitionInfoSender delegate_sender;

    public static Controller controller;
    public static Listener listener;

    public static void setDelegare_FetchHandInfo(delegate_HandInfoFetcher dele)
    {
        delegate_fetcher = dele;
    }

    public static void SampleMain()
    {
        // Create a sample listener and controller
        listener = new Listener();
        controller = new Controller();

        configList = FileReader.GetContentWithoutComment("config_list.txt");
        allSignLanguageConfig = new List<SignLanguageConfig>();
        foreach (string config in configList)
        {
            allSignLanguageConfig.Add(new SignLanguageConfig(config, config.Split('.')[0].Replace("configs/", "").Replace('/', '.')));
        }
        ConfigManager.GetCategory();
        controller.AddListener(listener);

        //controller.RemoveListener(listener);
        //controller.Dispose();
    }
}

public class ConfigManager
{
    public static List<string> list_Categories;
    public static void GetCategory ()
    {
        list_Categories = new List<string>();
        foreach(var config in CoreDetectionManager.allSignLanguageConfig)
        {
            if (!list_Categories.Contains(config.Category))
            {
                list_Categories.Add(config.Category);
            }
        }
    }

    public static SignLanguageConfig AddConfig(string Category, string Name)
    {
        SignLanguageConfig newConfig = new SignLanguageConfig();
        CoreDetectionManager.allSignLanguageConfig.Add(newConfig);
        string newConfigFilePath = Environment.CurrentDirectory + "\\configs\\" + Category +
                "\\" + Name + ".sign_language_config";
        Debug.WriteLine(newConfigFilePath);
        return newConfig;
    }

    public static void LearnFromHand(int handId, HandSimpled hand)
    {
        SignLanguageConfig thisConfig = CoreDetectionManager.allSignLanguageConfig[handId];
        for (int i = 0; i < 10; i++)
        {
            int min = thisConfig.BentPercentExpectation[i / 2, i % 2] - thisConfig.BentPercentPrecision[i / 2,i % 2];
            int max = thisConfig.BentPercentExpectation[i / 2, i % 2] + thisConfig.BentPercentPrecision[i / 2,i % 2];
            if (hand.bentPercent[i / 2, i % 2] < min)
            {
                min = hand.bentPercent[i / 2, i % 2];
                thisConfig.BentPercentPrecision[i / 2, i % 2] = (min + max) / 2;
                thisConfig.BentPercentExpectation[i / 2, i % 2] = (max - min) / 2;

            }
            if (hand.bentPercent[i / 2, i % 2] > max)
            {
                max = hand.bentPercent[i / 2, i % 2];
                thisConfig.BentPercentPrecision[i / 2, i % 2] = (min + max) / 2;
                thisConfig.BentPercentExpectation[i / 2, i % 2] = (max - min) / 2;
            }
        }
    }
    public static void AvoidHand(int handId, HandSimpled hand)
    {
        SignLanguageConfig thisConfig = CoreDetectionManager.allSignLanguageConfig[handId];
        for (int i = 0; i < 10; i++)
        {
            int min = thisConfig.BentPercentExpectation[i / 2, i % 2] - thisConfig.BentPercentPrecision[i / 2,i % 2];
            int max = thisConfig.BentPercentExpectation[i / 2, i % 2] + thisConfig.BentPercentPrecision[i / 2,i % 2];
            if (hand.bentPercent[i / 2, i % 2] >= min)
            {
                min = hand.bentPercent[i / 2, i % 2]+1;
                thisConfig.BentPercentPrecision[i / 2, i % 2] = (min + max) / 2;
                thisConfig.BentPercentExpectation[i / 2, i % 2] = (max - min) / 2;

            }
            if (hand.bentPercent[i / 2, i % 2] <= max)
            {
                max = hand.bentPercent[i / 2, i % 2]-1;
                thisConfig.BentPercentPrecision[i / 2, i % 2] = (min + max) / 2;
                thisConfig.BentPercentExpectation[i / 2, i % 2] = (max - min) / 2;
            }
        }
    }
    public static void ModifyConfig(int handId, SignLanguageConfig config)
    {

    }
    public static void ClearConfig(int handId)
    {
        SignLanguageConfig thisConfig = CoreDetectionManager.allSignLanguageConfig[handId];
        for(int i = 0; i < 10; i++)
        {
            thisConfig.BentPercentPrecision[i / 2, i % 2] = -1;
        }
    }
    public static List<SignLanguageConfig> GetHandFromCategory(string category)
    {
        List<SignLanguageConfig> result = new List<SignLanguageConfig>();
        foreach(SignLanguageConfig config in CoreDetectionManager.allSignLanguageConfig)
        {
            if (config.Category.Equals(category))
            {
                result.Add(config);
            }
        }
        return result;
    }

}